import os
import random
from datetime import datetime
from flask import Flask, render_template, request, session, redirect, url_for, jsonify
from config import config


def create_app() -> Flask:
    """Application factory for the guess-the-number game."""
    app = Flask(__name__)
    app.secret_key = config.SECRET_KEY

    @app.before_request
    def ensure_game_state() -> None:
        """Initialize game state in the session if it doesn't exist."""
        if "target_number" not in session:
            difficulty = session.get("difficulty", "medium")
            difficulty_config = config.DIFFICULTIES.get(difficulty, config.DIFFICULTIES["medium"])
            session["target_number"] = random.randint(difficulty_config["min"], difficulty_config["max"])
            session["attempts"] = 0
            session["guesses"] = []
            session["difficulty"] = difficulty
            session["min_range"] = difficulty_config["min"]
            session["max_range"] = difficulty_config["max"]

    @app.route("/", methods=["GET", "POST"])
    def index():
        message = ""
        hint = ""
        status = "playing"  # playing | success
        optimal_guesses = []
        stats = get_game_stats()

        if request.method == "POST":
            # 处理难度选择
            if "set_difficulty" in request.form:
                difficulty = request.form.get("difficulty", "medium")
                if difficulty in config.DIFFICULTIES:
                    session["difficulty"] = difficulty
                    session.pop("target_number", None)
                    session.pop("attempts", None)
                    session.pop("guesses", None)
                return redirect(url_for("index"))

            # 处理自定义范围
            elif "set_custom" in request.form:
                try:
                    min_val = int(request.form.get("min_range", "1"))
                    max_val = int(request.form.get("max_range", "100"))
                    if min_val >= max_val:
                        message = "最小值必须小于最大值！"
                    else:
                        session["difficulty"] = "custom"
                        session["target_number"] = random.randint(min_val, max_val)
                        session["attempts"] = 0
                        session["guesses"] = []
                        session["min_range"] = min_val
                        session["max_range"] = max_val
                        return redirect(url_for("index"))
                except ValueError:
                    message = "请输入有效的数字范围！"

            # 处理猜测
            else:
                guess_raw = request.form.get("guess", "").strip()
                if not guess_raw:
                    message = "请输入一个数字再提交～"
                else:
                    try:
                        guess = int(guess_raw)
                        min_range = session.get("min_range", 1)
                        max_range = session.get("max_range", 100)

                        # 服务器端输入验证
                        if guess < min_range or guess > max_range:
                            message = f"请输入 {min_range} 到 {max_range} 之间的数字！"
                        else:
                            target = session.get("target_number")
                            session["attempts"] = session.get("attempts", 0) + 1
                            session["guesses"].append(guess)

                            if guess < target:
                                message = "有点小，再大胆一点！"
                                hint = f"提示：目标数字更大 🔼 (范围: {guess}-{max_range})"
                            elif guess > target:
                                message = "有点大，稍微收敛一下～"
                                hint = f"提示：目标数字更小 🔽 (范围: {min_range}-{guess})"
                            else:
                                message = "恭喜你，猜对啦！🎉"
                                hint = f"你一共猜了 {session['attempts']} 次。"
                                status = "success"

                                # 保存到历史记录
                                save_game_result(
                                    session.get("difficulty", "medium"),
                                    session["attempts"],
                                    target
                                )
                    except ValueError:
                        message = "请输入正确的整数数字～"

        # 计算最优猜测次数（二分查找）
        min_range = session.get("min_range", 1)
        max_range = session.get("max_range", 100)
        optimal_guesses = calculate_optimal_guesses(min_range, max_range)

        return render_template(
            "index.html",
            attempts=session.get("attempts", 0),
            message=message,
            hint=hint,
            status=status,
            difficulty=session.get("difficulty", "medium"),
            min_range=min_range,
            max_range=max_range,
            difficulties=config.DIFFICULTIES,
            optimal_guesses=optimal_guesses,
            stats=stats,
        )

    @app.route("/reset")
    def reset():
        """Start a new round."""
        difficulty = session.get("difficulty", "medium")
        difficulty_config = config.DIFFICULTIES.get(difficulty, config.DIFFICULTIES["medium"])
        session["target_number"] = random.randint(difficulty_config["min"], difficulty_config["max"])
        session["attempts"] = 0
        session["guesses"] = []
        return redirect(url_for("index"))

    @app.route("/stats")
    def stats():
        """Get game statistics."""
        return jsonify(get_game_stats())

    @app.route("/history")
    def history():
        """Get game history."""
        history = session.get("game_history", [])
        return jsonify(history)

    return app


def get_game_stats():
    """Calculate game statistics from session."""
    game_history = session.get("game_history", [])
    if not game_history:
        return {
            "total_games": 0,
            "best_attempts": 0,
            "worst_attempts": 0,
            "average_attempts": 0,
        }

    attempts_list = [game["attempts"] for game in game_history]
    return {
        "total_games": len(game_history),
        "best_attempts": min(attempts_list),
        "worst_attempts": max(attempts_list),
        "average_attempts": round(sum(attempts_list) / len(attempts_list), 1),
    }


def save_game_result(difficulty, attempts, target):
    """Save game result to session history."""
    game_history = session.get("game_history", [])
    game_record = {
        "difficulty": difficulty,
        "attempts": attempts,
        "target": target,
        "timestamp": datetime.now().isoformat(),
    }
    game_history.append(game_record)

    # 只保留最近的记录
    if len(game_history) > config.MAX_HISTORY_RECORDS:
        game_history = game_history[-config.MAX_HISTORY_RECORDS:]

    session["game_history"] = game_history


def calculate_optimal_guesses(min_range, max_range):
    """Calculate optimal number of guesses using binary search."""
    range_size = max_range - min_range + 1
    if range_size <= 0:
        return 0

    import math
    return math.ceil(math.log2(range_size))


app = create_app()


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=config.PORT, debug=config.DEBUG)

